﻿#ifndef HTTPGET_H
#define HTTPGET_H

#include <QObject>
#include <QBasicTimer>
#include <QPointer>
#include <QSet>
#include <QVariant>

class QBuffer;
class QIODevice;
class QNetworkAccessManager;
class QNetworkReply;
class QTemporaryFile;

QNetworkAccessManager &manager();

class AbstractHttpGet : public QObject
{
    Q_OBJECT
public:
    static bool isTemporaryError(int err);
protected:
    explicit AbstractHttpGet(const QString& url);
    void abort();
signals:
    void internalCancelled();
protected slots:
    void slotFinished();
private slots:
    void slotReadyRead();
protected:
    bool canRetry(int err);
    virtual void readToBuffer(QNetworkReply *reply)=0;
    virtual void clearBuffer()=0;
    virtual void finishReading()=0;
    virtual void handleNetError(const QString& errStr = QString())=0;
    virtual void handleOtherError(const QString& errStr = QString())=0;
    virtual void internalDownloadProgress(qint64,qint64)=0;
    QString mUrl;
    QStringList errStrList;

    virtual void retry(QNetworkReply *reply=0);
    void createSlot(QNetworkReply *reply);
    QPointer<QNetworkReply> mReply;
    int mRetry;

    void timerEvent(QTimerEvent* e);
    QBasicTimer timer;
};

class PartialHttpGet;
class HttpGet : public AbstractHttpGet
{
    Q_OBJECT
public:
    explicit HttpGet(const QString& url, bool createFile=false, const QVariant& var = QVariant(), const QString& templ = QString());
    ~HttpGet();

    QString url() const { return mUrl; }
    QByteArray buffer();
    friend class PartialHttpGet;
signals:
    void finished(QString file,QVariant userData);
    void finished(QByteArray buffer,QVariant userData);
    void error(QVariant userData);
    void downloadProgress(qint64 received, qint64 total, QVariant userData);
private slots:
    void slotHeadFinished();
    void slotFirstFinished();
    void slotPartialFinished();
    void slotPartialNetError();
    void slotPartialOtherError();
    void slotPartialReaded();
    void internalDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private:
    void readToBuffer(QNetworkReply *reply);
    void clearBuffer();
    void finishReading();
    void handleOtherError(const QString& errStr = QString());
    void handlePartialError(QObject *sender);
    void tryFirst(QNetworkReply *reply);

    QPointer<QTemporaryFile> mTmpFile;
    QPointer<QBuffer> mBuffer;
    QSet<PartialHttpGet*> partialList;
    bool mCreateFile;
    qint64 mSize;
    const QString mTmpTemplate;
    bool error_;
protected:
    void tryHead(QNetworkReply *reply=0);
    QIODevice* openWriteDevice();
    void handleNetError(const QString& errStr = QString());
    QVariant mUserData;
};

class PartialHttpGet : public AbstractHttpGet
{
    Q_OBJECT
public:
    PartialHttpGet(const QString& url, qint64 begin, qint64 range, HttpGet& parent);

    friend class HttpGet;
signals:
    void partialFinished();
    void partialNetError();
    void partialOtherError();
    void partialReaded();
private slots:
    void internalDownloadProgress(qint64,qint64){}
private:
    void retry(QNetworkReply *reply=0);
    void readToBuffer(QNetworkReply *reply);
    void clearBuffer();
    void finishReading();
    void handleNetError(const QString& errStr = QString());
    void handleOtherError(const QString& errStr = QString());
    const qint64 start;
    const qint64 size;
    qint64 pos;
    HttpGet& control;
};

#endif // HTTPGET_H
